/**
 * \file   lexico.l
 * \author Fabian Jesus Machado
 * \date   Septiembre de 2011
 * 
 *  Este archivo es parte del proyecto NPASM, un ensamblador para
 * microcontroladores PIC con sintaxis Intel, desarrollado inicialmente
 * por Fabian Machado (fabianjsm@gmail.com, twitter@fabianjsm).
 *  El codigo fuente esta diponible en el repositorio SVN de Google:
 *      http://npasm.googlecode.com/svn/trunk.
 * 
 *  Es archivo posee definiciones y reglas para la generacion del
 * analizador lexico con FLEX.
 */
%{
#include <iostream>
#include <stdio.h>
#include "npasm.h"
#include "sintaxis.h"
#include "auxiliar.h"
#include "entorno.h"
#include "directiva.h"

static void saltar_comentario_c();
static int cadena_bruta(char *);
static int cadena_con_escapes(char *);
static int entero(const char *, int);
int identificador(const char *);
size_t leer_linea(void *buf, size_t max);

#ifdef YY_INPUT
#undef YY_INPUT
#endif
#define YY_INPUT(b,r,m) r = leer_linea(b,m);
size_t leer_linea(void *buf, size_t max);
char *asm_buf=0;
unsigned int asm_p, asm_uso;

%}

digito            [0-9]
letra             [a-zA-Z_]
separador         [ \t\v\f\r]
bin               0[bB][01]+
octal             0{digito}+
decimal           {digito}+
hex               0[xX][0-9a-fA-F]+
cadena            \"(\\.|[^\\"\n])*\"
cadena_raw        @{cadena}

%%

{bin}                       { return(entero(yytext, 2));        }
{hex}                       { return(entero(yytext, 16));       }
{octal}                     { return(entero(yytext, 8));        }
{decimal}                   { return(entero(yytext, 10));       }
{cadena}                    { return cadena_con_escapes(yytext);}
{cadena_raw}                { return cadena_bruta(yytext);      }
{letra}({letra}|{digito})*  { return identificador(yytext);     }
">>"                        { return(RR);                       }
"<<"                        { return(RL);                       }
"/*"                        { saltar_comentario_c();            }
({separador})+              {                                   }
";".*\n                     { return '\n';                      }
"$"                         { return PESO;                      }
\n                          { return '\n';                      }
.                           { return(yylval.num=*yytext);       }

%%

static void saltar_comentario_c()
{
	register int c;
	for (;;) {
		while ((c = yyinput()) != '*' && c != EOF && c != '\n')
			;
		if (c == EOF) {
			unput(c);
			yyerror("final prematuro del archivo");
			return;
		}
		if (c == '\n') {
			Entorno->nl();
			continue;
		}
		if (c == '*' ) {
			while ((c = yyinput()) == '*')
				;
			if (c == '/')
				return;
		}
	}
}

/**
 *  En el primer paso agrega la cadena a la trabla de string, en los
 * restantes se limita a devolver la referencia.
 *      Eliminamos las cadenas repetidas basandonos en que estas
 *     no seran modificadas (estaran en ROM).
 */
static void tabular_cadena(char *buf)
{
	if(Entorno->GetPaso() == 1) {
		size_t i, n;
		i = n = Entorno->TString->GetEntradas();
		if(n)
			i = Entorno->TString->FindString(buf);
		yylval.num = (i < n)? i: Entorno->TString->PutString(buf);
	} else
		yylval.num = Entorno->TString->FindString(buf);
	return;
}

static int cadena_bruta(char *s)
{
	s[yyleng - 1] = 0;
	tabular_cadena(s+2);
	s[yyleng - 1] = 1;
	return CADENA;
}

static int cadena_con_escapes(char *s)
{
	s[yyleng - 1] = 0;
	tabular_cadena(s+1);
	s[yyleng - 1] = 1;
	return CADENA;
}

/** Esta funcion es amiga de la clase \a entorno */
int identificador(const char *_ident)
{
	simbolo *s = Entorno->TS->Buscar(_ident);
	if(!s) {
		s = Entorno->TS->Insertar(_ident);
		s->SetTipo(IDENT);
	}
	yylval.sbl = s;
	return s->GetTipo();
}

static int entero(const char *strnum, int base)
{
	yylval.num = strtoulong(strnum, base);
	return NUM;
}

size_t leer_linea(void *buf, size_t max)
{
	errno=0;
	unsigned int i = 0;
	if(!asm_buf) {
		asm_buf = new char[YY_READ_BUF_SIZE];
		asm_p = asm_uso = 0;
	}
	for(;;) {
		if(asm_p == asm_uso) {
			asm_p=0;
			if(!(asm_uso = fread(asm_buf, 1, YY_READ_BUF_SIZE, yyin)))
				break;
		}
		if((((char *)buf)[i++] = asm_buf[asm_p++]) == '\n')
			break;
	}
	return i;
}

int yywrap()
{
	return restaurar_include();
}
